/*
 * SolverInformation.cpp
 *
 * Copyright 2012 Fabian Wenzelmann
 *
 * This file is part of Recognition-Procedures-for-Boolean-Functions.
 *
 * Recognition-Procedures-for-Boolean-Functions is free software: you
 * can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation,
 * either version 3 of the License, or (at your option) any later version.
 *
 * Recognition-Procedures-for-Boolean-Functions is distributed in the hope
 * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
 * Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Recognition-Procedures-for-Boolean-Functions.
 *
 * If not, see <http://www.gnu.org/licenses/>.
 */

#include "bfr/lpbCombinatorial/SolverInformation.hpp"

#include "bfr/CoefficientType.hpp"
#include "bfr/lpbCombinatorial/SplittingTree.hpp"

using std::pair;
using std::make_pair;

namespace bfr {

SolverInformation::SolverInformation()
    : coefficients(0), intervalFactors(0), intervals(0), columns(-1),
      coeffSum(0), repairBounds(0), computedBounds(0), repairFactors(0), culprits(0) { }

SolverInformation::~SolverInformation() {
    destroy();
}

void SolverInformation::init(const SplittingTree &tree) {
    destroy();
    columns = tree.getContext()->size();
    coefficients = new lpb_coefficient_t[columns];
    intervalFactors = new int[columns];
    intervals = new interval_t *[columns];
    coeffSum = new lpb_coefficient_t[columns];
    repairBounds = new pair<lpb_coefficient_t, lpb_coefficient_t>[columns];
    computedBounds = new pair<lpb_coefficient_t, lpb_coefficient_t>[columns];
    culprits = new pair<SplittingTreeNode *, SplittingTreeNode *>[columns];
    repairFactors = new int[columns];
    for (size_t i = 0; i < columns; i++) {
        coefficients[i] = -1;
        intervalFactors[i] = 1;
        intervals[i] = new interval_t[tree.getContext()->getColumnSize(i)];
        coeffSum[i] = -1;
        repairBounds[i] = make_pair(coeff_neg_infinity, coeff_pos_infinity);
        computedBounds[i] = make_pair(coeff_neg_infinity, coeff_pos_infinity);
	//	culprits[i] = make_pair(NULL, NULL);//No initialisation needed? JGS: clean!
        repairFactors[i] = 1;
    }
    repairFlag = 0;
    if (columns == 0)
        return;
    coeffSum[columns - 1] = 0;
}

void SolverInformation::print(const SplittingTree &t) {
    for (size_t i = 0; i < columns; i++) {
        std::cout << "Column " << i << std::endl;
        for (size_t j = 0; j < t.getContext()->getColumnSize(i); j++) {
            std::cout << '(' << intervals[i][j].first << ", " << intervals[i][j].second << ')' << std::endl;
        }
        std::cout << "===================================" << std::endl;
    }
}

lpb_coefficient_t SolverInformation::getCoefficient(size_t column) const {
    return coefficients[column];
}

// TODO: also works if we increase one of the coefficients?
void SolverInformation::setCoefficient(size_t column,
                                       lpb_coefficient_t value) {
    // calculate difference between old value and new value
    // set to zero initially
    lpb_coefficient_t diff = 0;
    const lpb_coefficient_t old = coefficients[column];
    if (old >= 0) {
        // coefficient has been set before
        // calculate the difference
        diff = value - old;
    }
    coefficients[column] = value;

    // update all coefficient sums
    if (column == (columns - 1)) {
        // last column, so just set the sum to value and return
        coeffSum[column] = value;
    } else {
        // set it for this column by hand, all columns before will only get an
        // update if they had an assignment before
        coeffSum[column] = coeffSum[column + 1] + value;
    }
    // not the last column, so update it assuming the following sums are correct
    if (diff == 0) {
        // nothing has changed / first assignment
        return;
    }
    size_t pos = column;
    while ((pos > 0) && (coeffSum[pos - 1] >= 0)) {
        --pos;
        coeffSum[pos] += diff;
    }
}

interval_t SolverInformation::getInterval(const SplittingTreeNode &n) const {
    const size_t col = n.getColumn();
    interval_t &interval = intervals[col][n.getRow()];
    size_t intervalFactor = intervalFactors[col];
    return interval_t(interval.first * intervalFactor,
                      interval.second * intervalFactor);
}

void SolverInformation::setInterval(const SplittingTreeNode &n,
                                    lpb_coefficient_t s,
                                    lpb_coefficient_t b) {

    // TODO don't we have to set the intervalFactor to 1?
    // but actually I dont't think so
    // we can assume that all coefficients in this column have
    // already been set
    // but if not we must set it to something else...
    // maybe this will cause problems in backtracking... I really
    // have to think about it
    // maybe we should add a "fix column values" method
    // that sets the coefficients again by iterating over them
    // and setting the factor to 1
    // then we have to ensure that this method is called in the right
    // moment, of course
    intervals[n.getColumn()][n.getRow()] = interval_t(s, b);
}

  //HERE
interval_t SolverInformation::solveConflict(size_t column,
                                            lpb_coefficient_t max,
                                            lpb_coefficient_t min) {
    // on position column we only have to update the interval factors,
    // but not the coefficients
    // in all other columns we have to update both
    intervalFactors[column] *= 2;
    repairFactors[column] *= 2;
    computedBounds[column].first *= 2;
    computedBounds[column].second *= 2;
    for (size_t i = column + 1; i < columns; i++) {
        coefficients[i] *= 2;
        intervalFactors[i] *= 2;
        coeffSum[i] *= 2;
        repairFactors[i] *= 2;
	computedBounds[i].first *= 2;
	computedBounds[i].second *= 2;
    }
    size_t pos = column;
    // TODO!!!!!
    // we must specify much better what this class does...
    while ((pos > 0) && (coeffSum[pos - 1] >= 0)) {
            --pos;
            coeffSum[pos] *= 2;
        }
    return interval_t(max * 2, min * 2);
}

// TODO
lpb_coefficient_t SolverInformation::getSumFrom(size_t column) const {
    //return coeffSum[column];
    lpb_coefficient_t result = 0;
    for (size_t i = column; i < columns; i++) {
        result += getCoefficient(i);
    }
    return result;
}

void SolverInformation::destroy() {
    if (coefficients != 0) {
        delete[] coefficients;
        coefficients = 0;
    }
    if (intervalFactors != 0) {
        delete[] intervalFactors;
        intervalFactors = 0;
    }
    if (intervals != 0) {
        for (size_t i = 0; i < columns; i++) {
            if (intervals[i] != 0) {
                delete[] intervals[i];
            }
        }
        delete[] intervals;
        intervals = 0;
    }
    if (coeffSum != 0) {
        delete[] coeffSum;
        coeffSum = 0;
    }
    columns = -1;
    if (repairBounds != 0)
        delete[] repairBounds;
    if (repairFactors != 0)
        delete[] repairFactors;
}

void SolverInformation::resetIntervalFactor(size_t column) {
    intervalFactors[column] = 1;
}


pair<lpb_coefficient_t, lpb_coefficient_t>
    SolverInformation::getRepairBound(size_t column) const {
    const pair<lpb_coefficient_t, lpb_coefficient_t> &current = repairBounds[column];
    return make_pair(current.first * repairFactors[column],
                      current.second * repairFactors[column]);
}

 bool SolverInformation::setLowerRepairBound(size_t column, lpb_coefficient_t val) {
   //JGS: undo all repair bounds to the left. Maybe wasteful as this could have been done as one goes along.
   //But that should be negligible. 
    for (size_t i = 0; i < column; i++) {
      repairBounds[i] = make_pair(coeff_neg_infinity, coeff_pos_infinity);
    }
    pair<lpb_coefficient_t, lpb_coefficient_t> &current = repairBounds[column];
    current.second *= repairFactors[column];
    current.first *= repairFactors[column];
    if ((!(current.first < val) && (current.first != coeff_neg_infinity)) || (val == coeff_pos_infinity) || (val == coeff_neg_infinity)) {//JGS: simplify???
     return false;
    }
    current.first = val;
    repairFactors[column] = 1;
    return true;
}

bool SolverInformation::setUpperRepairBound(size_t column, lpb_coefficient_t val) {
    //JGS: undo all repair bounds to the left. Maybe wasteful as this could have been done as one goes along.
    //But that should be negligible. 
    for (size_t i = 0; i < column; i++) {
      repairBounds[i] = make_pair(coeff_neg_infinity, coeff_pos_infinity);
    }
    pair<lpb_coefficient_t, lpb_coefficient_t> &current = repairBounds[column];
    current.first *= repairFactors[column];
    current.second *= repairFactors[column];
    if ((! (current.second > val) && (current.second != coeff_pos_infinity)) || (val == coeff_neg_infinity) || (val == coeff_pos_infinity)) {//JGS: simplify???
      return false;
    }
    current.second = val;
    repairFactors[column] = 1;
    return true;
}

pair<lpb_coefficient_t, lpb_coefficient_t>
    SolverInformation::getComputedBound(size_t column) const {
    const pair<lpb_coefficient_t, lpb_coefficient_t> &current = computedBounds[column];
    return make_pair(current.first, current.second);
}

void SolverInformation::setLowerComputedBound(size_t column, lpb_coefficient_t val) {
    pair<lpb_coefficient_t, lpb_coefficient_t> &current = computedBounds[column];
    current.first = val;
}

void SolverInformation::setUpperComputedBound(size_t column, lpb_coefficient_t val) {
    pair<lpb_coefficient_t, lpb_coefficient_t> &current = computedBounds[column];
    current.second = val;
}


  pair<SplittingTreeNode *, SplittingTreeNode *>
    SolverInformation::getCulprit(size_t column) const {
    return culprits[column];
}

void SolverInformation::setMinCulprit(size_t column, SplittingTreeNode *minCulprit) {
    pair<SplittingTreeNode *, SplittingTreeNode *> &current = culprits[column];
    current.first = minCulprit;
}

void SolverInformation::setMaxCulprit(size_t column, SplittingTreeNode *maxCulprit) {
    pair<SplittingTreeNode *, SplittingTreeNode *> &current = culprits[column];
    current.second = maxCulprit;
}

void SolverInformation::setRepairFlag(size_t flag) {
    repairFlag = flag;
}    

size_t SolverInformation::getRepairFlag() {
    return repairFlag;
}    


}
